//The contents of this file are subject to the Mozilla Public License Version 1.1
//(the "License"); you may not use this file except in compliance with the
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.
package org.columba.mail.gui.config.filter;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;

import org.columba.api.gui.frame.IFrameMediator;
import org.columba.core.facade.DialogFacade;
import org.columba.core.filter.Filter;
import org.columba.core.filter.FilterList;
import org.columba.core.filter.IFilter;
import org.columba.core.filter.IFilterList;
import org.columba.core.gui.base.ButtonWithMnemonic;
import org.columba.core.gui.base.SingleSideEtchedBorder;
import org.columba.core.help.HelpManager;
import org.columba.core.xml.XmlElement;
import org.columba.core.xml.XmlIO;
import org.columba.mail.folder.IMailbox;
import org.columba.mail.util.MailResourceLoader;

/**
 * JDialog that displays Filter actions for one message folder.
 * 
 * @author Erik Mattsson
 */

public class ConfigFrame extends JDialog implements ListSelectionListener,
		ActionListener {
	/*
	 * private JTextField textField; private JPanel leftPanel; private
	 * JTabbedPane rightPanel; private JButton addButton; private JButton
	 * removeButton; private JButton editButton; private JButton upButton;
	 * private JButton downButton;
	 * 
	 * private JFrame frame;
	 */
	private FilterListTable listView;

	// private AdapterNode actNode;
	private IFilterList filterList;

	// private Filter filter;
	// private JDialog dialog;
	private JTextField nameTextField = new JTextField();

	private JButton addButton;

	private JButton removeButton;

	private JButton editButton;

	/*
	 * private JButton enableButton; private JButton disableButton;
	 */
	private JButton moveupButton;

	private JButton movedownButton;

	private JButton importButton;

	private JButton exportButton;

	private IMailbox folder;

	private IFrameMediator mediator;

	/**
	 * Constructs a ConfigFrame for the specified folder.
	 * 
	 * @param messageFolder
	 *            folder to set filter actions for.
	 */
	public ConfigFrame(IFrameMediator mediator, IMailbox messageFolder) {
		super(mediator.getView().getFrame(), true);
		this.mediator = mediator;

		folder = messageFolder;

		setTitle(MailResourceLoader.getString("dialog", "filter",
				"dialog_title"));
		filterList = folder.getFilterList();

		initComponents();
		pack();
		setLocationRelativeTo(null);
		setVisible(true);
	}

	/**
	 * Returns the <code>Filter</code> that is selected in the list.
	 * 
	 * @return null if there is no filter selected; otherwise returns the
	 *         selected <code>Filter</code>.
	 */
	public IFilter getSelected() {
		IFilter filter = null;
		ListSelectionModel model = listView.getSelectionModel();

		if (!model.isSelectionEmpty()) {
			int index = model.getAnchorSelectionIndex();
			filter = filterList.get(index);
		}

		return filter;
	}

	/**
	 * Selects the filter in the list.
	 * 
	 * @param f
	 *            filter to select in list.
	 */
	public void setSelected(IFilter f) {
		int index = filterList.indexOf(f);

		if (index != -1) {
			listView.getSelectionModel().setSelectionInterval(index, index);
		}
	}

	/**
	 * Inits the GUI components.
	 */
	private void initComponents() {
		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BorderLayout());
		mainPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
		getContentPane().add(mainPanel);

		addButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "add_filter"));
		addButton.setActionCommand("ADD");
		addButton.addActionListener(this);

		removeButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "remove_filter"));
		removeButton.setActionCommand("REMOVE");
		removeButton.setEnabled(false);
		removeButton.addActionListener(this);

		editButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "edit_filter"));
		editButton.setActionCommand("EDIT");
		editButton.setEnabled(false);
		editButton.addActionListener(this);

		moveupButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "moveup"));
		moveupButton.setActionCommand("MOVEUP");
		moveupButton.setEnabled(false);
		moveupButton.addActionListener(this);

		movedownButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "movedown"));
		movedownButton.setActionCommand("MOVEDOWN");
		movedownButton.setEnabled(false);
		movedownButton.addActionListener(this);

		importButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "import"));
		importButton.setActionCommand("IMPORT");
		importButton.setEnabled(true);
		importButton.addActionListener(this);

		exportButton = new ButtonWithMnemonic(MailResourceLoader.getString(
				"dialog", "filter", "export"));
		exportButton.setActionCommand("EXPORT");
		exportButton.setEnabled(false);
		exportButton.addActionListener(this);

		// top panel
		JPanel topPanel = new JPanel();
		topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));

		GridBagLayout gridBagLayout = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();

		// topPanel.setLayout( );
		JPanel topBorderPanel = new JPanel();
		topBorderPanel.setLayout(new BorderLayout());

		// topBorderPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5,
		// 0));
		topBorderPanel.add(topPanel);

		// mainPanel.add( topBorderPanel, BorderLayout.NORTH );
		JLabel nameLabel = new JLabel("name");
		nameLabel.setEnabled(false);
		topPanel.add(nameLabel);

		topPanel.add(Box.createRigidArea(new java.awt.Dimension(10, 0)));
		topPanel.add(Box.createHorizontalGlue());

		nameTextField.setText("name");
		nameTextField.setEnabled(false);
		topPanel.add(nameTextField);

		Component glue = Box.createVerticalGlue();
		c.anchor = GridBagConstraints.EAST;
		c.gridwidth = GridBagConstraints.REMAINDER;

		// c.fill = GridBagConstraints.HORIZONTAL;
		gridBagLayout.setConstraints(glue, c);

		gridBagLayout = new GridBagLayout();
		c = new GridBagConstraints();

		JPanel eastPanel = new JPanel(gridBagLayout);
		mainPanel.add(eastPanel, BorderLayout.EAST);

		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.gridwidth = GridBagConstraints.REMAINDER;
		gridBagLayout.setConstraints(addButton, c);
		eastPanel.add(addButton);

		Component strut1 = Box.createRigidArea(new Dimension(30, 6));
		gridBagLayout.setConstraints(strut1, c);
		eastPanel.add(strut1);

		gridBagLayout.setConstraints(editButton, c);
		eastPanel.add(editButton);

		Component strut = Box.createRigidArea(new Dimension(30, 6));
		gridBagLayout.setConstraints(strut, c);
		eastPanel.add(strut);

		gridBagLayout.setConstraints(removeButton, c);
		eastPanel.add(removeButton);

		strut = Box.createRigidArea(new Dimension(30, 12));
		gridBagLayout.setConstraints(strut, c);
		eastPanel.add(strut);

		gridBagLayout.setConstraints(moveupButton, c);
		eastPanel.add(moveupButton);

		strut = Box.createRigidArea(new Dimension(30, 6));
		gridBagLayout.setConstraints(strut, c);
		eastPanel.add(strut);

		gridBagLayout.setConstraints(movedownButton, c);
		eastPanel.add(movedownButton);

		strut = Box.createRigidArea(new Dimension(30, 12));
		gridBagLayout.setConstraints(strut, c);
		eastPanel.add(strut);

		gridBagLayout.setConstraints(importButton, c);
		eastPanel.add(importButton);

		strut = Box.createRigidArea(new Dimension(30, 6));
		gridBagLayout.setConstraints(strut, c);
		eastPanel.add(strut);

		gridBagLayout.setConstraints(exportButton, c);
		eastPanel.add(exportButton);

		glue = Box.createVerticalGlue();
		c.fill = GridBagConstraints.BOTH;
		c.weighty = 1.0;
		gridBagLayout.setConstraints(glue, c);
		eastPanel.add(glue);

		// centerpanel
		JPanel centerPanel = new JPanel(new BorderLayout());
		centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 6));
		listView = new FilterListTable(filterList, this);
		listView.getSelectionModel().addListSelectionListener(this);
		listView.addMouseListener(new MouseTableListener());
		listView.setTransferHandler(new FilterTransferHandler());
		listView.setDragEnabled(true);

		JScrollPane scrollPane = new JScrollPane(listView);
		scrollPane.setPreferredSize(new Dimension(300, 250));
		scrollPane.getViewport().setBackground(Color.white);
		scrollPane.setTransferHandler(new FilterTransferHandler());
		centerPanel.add(scrollPane);

		mainPanel.add(centerPanel);

		JPanel bottomPanel = new JPanel(new BorderLayout());
		bottomPanel.setBorder(new SingleSideEtchedBorder(SwingConstants.TOP));

		JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 6, 0));
		buttonPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));

		ButtonWithMnemonic closeButton = new ButtonWithMnemonic(
				MailResourceLoader.getString("global", "close"));
		closeButton.setActionCommand("CLOSE"); //$NON-NLS-1$
		closeButton.addActionListener(this);
		buttonPanel.add(closeButton);

		ButtonWithMnemonic helpButton = new ButtonWithMnemonic(
				MailResourceLoader.getString("global", "help"));
		buttonPanel.add(helpButton);
		bottomPanel.add(buttonPanel, BorderLayout.EAST);
		getContentPane().add(bottomPanel, BorderLayout.SOUTH);
		getRootPane().setDefaultButton(closeButton);
		getRootPane().registerKeyboardAction(this, "CLOSE",
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);

		// associate with JavaHelp
		HelpManager.getInstance().enableHelpOnButton(helpButton,
				"organising_and_managing_your_email_3");
		HelpManager.getInstance().enableHelpKey(getRootPane(),
				"organising_and_managing_your_email_3");
	}

	/** {@inheritDoc} */
	public void valueChanged(ListSelectionEvent e) {
		if (e.getValueIsAdjusting()) {
			return;
		}

		DefaultListSelectionModel theList = (DefaultListSelectionModel) e
				.getSource();

		if (theList.isSelectionEmpty()) {
			removeButton.setEnabled(false);
			editButton.setEnabled(false);
			movedownButton.setEnabled(false);
			moveupButton.setEnabled(false);
			exportButton.setEnabled(false);
		} else {
			removeButton.setEnabled(true);
			exportButton.setEnabled(true);

			int[] selectedRows = listView.getSelectedRows();
			int minIndex = selectedRows[0];
			int maxIndex = selectedRows[selectedRows.length - 1];

			if (minIndex == maxIndex) {
				editButton.setEnabled(true);
			} else {
				editButton.setEnabled(false);
			}

			if (minIndex == 0) {
				moveupButton.setEnabled(false);
			} else {
				moveupButton.setEnabled(true);
			}

			if (maxIndex == (filterList.count() - 1)) {
				movedownButton.setEnabled(false);
			} else {
				movedownButton.setEnabled(true);
			}
		}
	}

	/**
	 * Shows the edit filter dialog. Method returns false if the dialog was
	 * cancelled by the user or if the specified filter was null. It returns
	 * true if the user has pressed <code>Close</code> in order to save the
	 * filter into the filter list.
	 * 
	 * @param filter
	 *            the filter to edit.
	 * @return true if the filter dialog was closed (not cancelled); false if
	 *         the editing was cancelled or if the argument was null.
	 */
	public boolean showFilterDialog(IFilter filter) {
		boolean saveFilter = false;

		if (filter != null) {
			FilterDialog dialog = new FilterDialog(mediator, filter);
			saveFilter = !dialog.wasCancelled();
		}

		return saveFilter;
	}

	/**
	 * {@inheritDoc}
	 */
	public void actionPerformed(ActionEvent e) {
		String action = e.getActionCommand();

		if (action.equals("CLOSE")) {

			setVisible(false);
		} else if (action.equals("ADD")) {
			IFilter filter = FilterList.createDefaultFilter();

			if (showFilterDialog(filter)) {
				filterList.add(filter);
				listView.update();
				setSelected(filter);
			}
		} else if (action.equals("REMOVE")) {
			int[] selectedRows = listView.getSelectedRows();

			// Must go backwards or else the list will remove the wrong filters.
			for (int i = selectedRows.length - 1; i >= 0; i--) {
				filterList.remove(selectedRows[i]);
			}

			listView.update();
		} else if (action.equals("EDIT")) {
			editSelectedFilter();
		} else if (action.equals("MOVEUP")) {
			int[] selectedRows = listView.getSelectedRows();

			for (int i = 0; i < selectedRows.length; i++) {
				filterList.move(selectedRows[i], -1);
				selectedRows[i]--;
			}

			listView.setRowSelection(selectedRows);
		} else if (action.equals("MOVEDOWN")) {
			int[] selectedRows = listView.getSelectedRows();

			// Must go backwards or else the filters will swap places with each
			// other.
			for (int i = selectedRows.length - 1; i >= 0; i--) {
				filterList.move(selectedRows[i], 1);
				selectedRows[i]++;
			}

			listView.setRowSelection(selectedRows);
		} else if (action.equals("EXPORT")) {
			exportFilterList();
		} else if (action.equals("IMPORT")) {
			importFilterList();
		}
	}

	/**
	 * Exports the selected filters into a user selected file.
	 */
	private void exportFilterList() {
		int[] selectedRows = listView.getSelectedRows();
		FilterList newFilterList = new FilterList();

		for (int i = 0; i < selectedRows.length; i++) {
			newFilterList.add(filterList.get(i));
		}

		// ask the user about the destination file
		JFileChooser chooser = new JFileChooser();
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		chooser.setMultiSelectionEnabled(false);
		chooser.setFileFilter(new XmlFileFilter());

		int result = chooser.showSaveDialog(this);

		if (result == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();

			XmlIO xmlIO = new XmlIO(newFilterList.getRoot());

			try {
				xmlIO.write(new FileOutputStream(file));
			} catch (FileNotFoundException fnfe) {
				DialogFacade.showExceptionDialog(fnfe);
			} catch (IOException ioe) {
				DialogFacade.showExceptionDialog(ioe);
			}
		}
	}

	/**
	 * Imports filters from a File. This method pops up a dialog asking which
	 * file it should import filters from.
	 */
	private void importFilterList() {
		// ask the user about the destination file
		JFileChooser chooser = new JFileChooser();
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		chooser.setMultiSelectionEnabled(false);
		chooser.setFileFilter(new XmlFileFilter());

		int result = chooser.showOpenDialog(this);

		if (result == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();
			XmlIO xmlIO;

			try {
				xmlIO = new XmlIO(file.toURL());

				if (xmlIO.load()) {
					XmlElement root = xmlIO.getRoot().getElement(
							FilterList.XML_NAME);

					if (root != null) {
						FilterList newFilterList = new FilterList(root);
						filterList.addAll(newFilterList);
						listView.update();
					} else {
						JOptionPane.showMessageDialog(this,
								"Could not import filters from the selected file. Make sure it"
										+ " contains Columba filters.",
								"Error while importing filters",
								JOptionPane.ERROR_MESSAGE);
					}
				} else {
					JOptionPane
							.showMessageDialog(
									this,
									"Could not parse the selected file. Make sure it"
											+ " is an XML file containing Columba filters.",
									"Error while parsing file",
									JOptionPane.ERROR_MESSAGE);
				}
			} catch (MalformedURLException mue) {
				DialogFacade.showExceptionDialog(mue);
			}
		}
	}

	/**
	 * Opens the filter edit dialog for the selected filter.
	 */
	private void editSelectedFilter() {
		IFilter oldFilter = getSelected();

		if (oldFilter != null) {
			Filter newFilter = (Filter) oldFilter.clone();

			if (showFilterDialog(newFilter)) {
				int index = listView.getSelectedRow();
				filterList.insert(newFilter, index);
				filterList.remove(index + 1);
				listView.update(index);
			}
		}
	}

	/**
	 * Mouse adapter that listens for double clicks on a filter in the filter
	 * table.
	 */
	private class MouseTableListener extends MouseAdapter {
		/** {@inheritDoc} */
		public void mouseClicked(MouseEvent e) {
			if ((e.getButton() == MouseEvent.BUTTON1)
					&& (e.getClickCount() >= 2)) {
				editSelectedFilter();
			}
		}
	}

	/**
	 * File filter that only accepts XML files (or those ending with it).
	 * 
	 * @author redsolo
	 */
	private class XmlFileFilter extends FileFilter {
		/** {@inheritDoc} */
		public boolean accept(File f) {
			boolean fileIsOk = false;

			if (f.isDirectory()) {
				fileIsOk = true;
			} else {
				fileIsOk = f.getName().toLowerCase().endsWith("xml");
			}

			return fileIsOk;
		}

		/** {@inheritDoc} */
		public String getDescription() {
			return "XML file filter";
		}
	}
}